home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 11 / develop 11 code / MultiBuffer / MultiBuffer Source / AIFFGoodies.c next >
Encoding:
C/C++ Source or Header  |  1992-07-15  |  15.0 KB  |  405 lines  |  [TEXT/MPS ]

  1. /*
  2. **        AIFFGoodies
  3. **
  4. **            This module contains the routines that pertian to opening, reading and otherwise dealing
  5. **                with AIFF Files
  6. **
  7. **            (NMD)    9/16/91        created
  8. **            (NMD)    9/16/91        OpenAIFFFile
  9. **            (NMD)    9/16/91        GetAIFFHeaderInfo
  10. **                                    • Changed to take the paramBlock as an argument and return a long
  11. **                                        indicating the total length of the AIFF data.
  12. **            (NMD)    9/20/91        Fixed GetAIFFHeaderInfo to (properly) deal with only 8 bit monophonic samples.
  13. **            (NMD)    10/3/91        Changed GetAIFFHeaderInfo so it doesn't stomp on the samplePtr & lenght...
  14. **            (NMD)    11/29/91    RecordAIFFFile  allows you to record an aiff file to disk...
  15. */
  16.  
  17.  
  18. #pragma load "MacHeaders"
  19.  
  20. #include <FixMath.h>
  21.  
  22. #ifndef __MAININCLUDES__
  23. #include "MainApp.h"
  24. #endif
  25.  
  26.  
  27. #define kChunkBufferSize     128
  28.  
  29. #define kChunkHeaderSize    8
  30.  
  31. enum {                                                                                                        // Sample sizes
  32.     k8bits                     = 8,
  33.     k16bits                 = 16,
  34.     k24bits                 = 24,
  35.     k32bits                 = 32
  36. };
  37.  
  38. // these are bit positions for a long flag
  39.  
  40. #define kFORM                    (1<<0)
  41. #define kFormatVersion            (1<<1)
  42. #define kCommon                    (1<<2)
  43. #define kSoundData                (1<<3)
  44. #define kMarker                    (1<<4)
  45. #define kInstrument                (1<<5)
  46. #define kMIDIData                (1<<6)
  47. #define kAudioRecording            (1<<7)
  48. #define kApplicationSpecific    (1<<8)
  49. #define kComment                (1<<9)
  50. #define kName                    (1<<10)
  51. #define kAuthor                    (1<<12)
  52. #define kCopyright                (1<<13)
  53. #define kAnnotation                (1<<14)
  54.  
  55.  
  56. typedef union {
  57.     ChunkHeader                    generic;
  58.     ContainerChunk                container;
  59.     FormatVersionChunk            formatVersion;
  60.     CommonChunk                    common;
  61.     ExtCommonChunk                extCommon;
  62.     SoundDataChunk                soundData;
  63.     MarkerChunk                    marker;
  64.     InstrumentChunk                instrument;
  65.     MIDIDataChunk                midiData;
  66.     AudioRecordingChunk            audioRecording;
  67.     CommentsChunk                comments;
  68.     TextChunk                    text;
  69. } ChunkTemplate, *ChunkTemplatePtr;
  70.  
  71.  
  72. //
  73. //        Given an AIFF File gets the sample rate and other relavent pieces of information.
  74. //            Leaves the file mark at the beginning of the AIFF Sound Data Chunk.  Please note
  75. //            that I'm placing some pretty severe restrictions on the types of samples that this
  76. //            will actually deal with - 8 bit monophonic, to be more specific...
  77. //
  78. long GetAIFFHeaderInfo (short frefNum, SoundHeaderPtr theHeader)
  79. {
  80.     OSErr                    err;                            // error container
  81.     long                    filePosition;                    // current position in file
  82.     long                    dataStart = 0;                    // where snd data starts
  83.     long                    dataSize = 0;                    // how big snd data is
  84.  
  85.     long                    byteCount;                        // # if bytes to get from file
  86.     char                    chunkBuffer[kChunkBufferSize];    // buffer for chunk data
  87.  
  88.     unsigned short            chunkFlags = 0;                    // remember chunks we've seen
  89.     ChunkTemplatePtr        template;                        // ...for ease of access
  90.  
  91.     //    Keep track of the beginning of the file
  92.     err = GetFPos (frefNum, &filePosition);
  93.     Assert ((err != noErr), "\pCouldn't get file position");
  94.  
  95.     //  Now for the fun part ! We're going to assume that these are generic, non-compressed
  96.     //        AIFF files and parse accordingly...
  97.  
  98.     do {
  99.  
  100.         //    Position ourselves at the beginning of the next chunk and read in
  101.         //    a hunk-o-data...
  102.         err = SetFPos (frefNum, fsFromStart, filePosition);
  103.         Assert ((err != noErr), "\pCouldn't set file position");
  104.         if (err != noErr)
  105.             break;
  106.  
  107.         byteCount = kChunkBufferSize;
  108.         err = FSRead (frefNum, &byteCount, chunkBuffer);                                                        // read a chunk
  109.         Assert ((err != noErr && err != eofErr), "\pBombed out reading file");
  110.         if ( (err != noErr) && (err != eofErr) )
  111.             break;
  112.  
  113.         //    Now, position the template over the data...
  114.         template = (ChunkTemplatePtr) chunkBuffer;
  115.  
  116.         // assume a failure and break out of the do {} while loop if the next case isn't found.
  117.         // if the case is found and no other error is detected, then each case needs to set noErr
  118.         err = badFileFormat;                                
  119.         switch (template->generic.ckID) {
  120.         
  121.             case FORMID :                                                                                // Format Version Chunk?
  122.                 DebugMessage ("\pfound the form chunk");
  123.                 Assert ((chunkFlags & kFORM), "\pOH NO!! Found a duplicate FORM Chunk!!");
  124.                 Assert ((template->container.formType != AIFFID), "\pthis isn't a non-compressed AIFF file");
  125.  
  126.                 //    make sure that this is a standard, noncompressed AIFF file.
  127.                 if ( !(chunkFlags & kFORM) && (template->container.formType == AIFFID) ) {                                                                //  see if this chunk already exists
  128.                     chunkFlags |= kFORM;                                                                //        otherwise mark it found
  129. //                    filePosition += template->container.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  130.                     filePosition += 12;
  131.                     err = noErr;
  132.                 }
  133.                 break;
  134.  
  135.  
  136.             case FormatVersionID :                                                                        // Format Version Chunk?
  137.                 DebugMessage ("\pfound the container chunk");
  138.                 Assert ((chunkFlags & kFormatVersion), "\pOH NO!! Found a duplicate format version Chunk!!");
  139.  
  140.                 if ( !(chunkFlags & kFormatVersion) ) {                                                        //  see if this chunk already exists
  141.                     chunkFlags |= kFormatVersion;                                                        //        otherwise mark it found
  142.                     filePosition += template->formatVersion.ckSize + kChunkHeaderSize;                    //        calculate next chunk's position
  143.                     err = noErr;
  144.                 }
  145.                 break;
  146.  
  147.  
  148.             case CommonID :
  149.                 DebugMessage ("\pfound the common chunk");
  150.                 Assert ((chunkFlags & kCommon), "\pOH NO!! Found a duplicate common Chunk!!");
  151.                  Assert ((template->common.numChannels != 1), "\pToo many channels");
  152.                 Assert ((template->common.sampleSize != k8bits), "\pWrong sample size");
  153.  
  154.                 if (     !(chunkFlags & kCommon)
  155.                         && (template->common.numChannels == 1)
  156.                         && (template->common.sampleSize == k8bits)) {                                    //  see if this chunk already exists
  157.                         
  158.                     chunkFlags |= kCommon;                                                                //        otherwise mark it found
  159.                     filePosition += template->common.ckSize + kChunkHeaderSize;                            //        calculate next chunk's position
  160.  
  161.                     //    Now that we theoretically have all the relavant information, put it into a header the sound manager
  162.                     //    can deal with.  Note that we're not returning the length of the file in theHeader->length, because it is
  163.                     //    EXTREMELY unlikely that the AIFF file is small enough to feed the SM without resorting to multiple buffers.
  164.                     //       • Note that we're not modifying the header's samplePtr or lenght, cause those may contain valid entries!!
  165.                     theHeader->sampleRate        = X2Fix (template->common.sampleRate);
  166.                     theHeader->loopStart        = 0;
  167.                     theHeader->loopEnd            = 0;
  168.                     theHeader->encode            = 0;
  169.                     theHeader->baseFrequency    = 60;
  170.  
  171.                     //    Perhaps some explanation is in order here:  we're interested in how many bytes of data we're going to
  172.                     //        be dealing with later on.  Since AIFF is interleaved, multiply the number of channels present by
  173.                     //        the number of frames in the sample, by the number of bits per sample.  Divide by 8 bits/byte and
  174.                     //        you done got the number of bytes.  So a stereo 8 bit sample actually looks like this:
  175.                     //
  176.                     //        |-------------  frame 1  -----------|  |-------------  frame 2  -----------|  ...
  177.                     //
  178.                     //        [{ch1 sample pt 1} {ch2 sample pt 1}]  [{ch1 sample pt 2} {ch2 sample pt 2}]  ...
  179.  
  180.                     dataSize = (template->common.numChannels * template->common.numSampleFrames * template->common.sampleSize) / 8;
  181.                     err = noErr;
  182.                 }
  183.                 break;
  184.  
  185.  
  186.             case SoundDataID:
  187.                 DebugMessage ("\pfound the sound chunk");
  188.                 Assert ((chunkFlags & kSoundData), "\pOH NO!! Found a duplicate sound data Chunk!!");
  189.  
  190.                 if ( !(chunkFlags & kSoundData) ) {                                                            //  see if this chunk already exists
  191.  
  192.                     //    Let's remember where the Sound Data starts, so we can find the position in the file later.
  193.                     //  The mark will be positioned at the beginning of the chunk, so move 16 bytes past to get past
  194.                     //    the header information to the data.
  195.                     dataStart = filePosition + 16;
  196.  
  197.                     chunkFlags |= kSoundData;                                                            //        otherwise mark it found
  198.                     filePosition += template->soundData.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  199.                     err = noErr;
  200.                 }
  201.                 break;
  202.  
  203.  
  204.  
  205.  
  206.             //
  207.             //    The following list of chunks we don't care about, so we'll just skip over them.
  208.             //
  209.             case MarkerID:
  210.                 DebugMessage ("\pfound a marker chunk");
  211.                 Assert ((chunkFlags & kMarker), "\pOH NO!! Found a duplicate marker Chunk!!");
  212.  
  213.                 if ( !(chunkFlags & kMarker) ) {                                                            //  see if this chunk already exists
  214.                     chunkFlags |= kMarker;                                                                //        otherwise mark it found
  215.                     filePosition += template->marker.ckSize + kChunkHeaderSize;                            //        calculate next chunk's position
  216.                     err = noErr;
  217.                 }
  218.                 break;
  219.  
  220.  
  221.             case CommentID:
  222.                 DebugMessage ("\pfound a comment chunk");
  223.                 Assert ((chunkFlags & kComment), "\pOH NO!! Found a duplicate comment Chunk!!");
  224.  
  225.                 if ( !(chunkFlags & kComment) ) {                                                            //  see if this chunk already exists
  226.                     chunkFlags |= kComment;                                                                //        otherwise mark it found
  227.                     filePosition += template->marker.ckSize + kChunkHeaderSize;                            //        calculate next chunk's position
  228.                     err = noErr;
  229.                 }
  230.                 break;
  231.  
  232.  
  233.             case InstrumentID:
  234.                 DebugMessage ("\pfound an instrument chunk");
  235.                 Assert ((chunkFlags & kInstrument), "\pOH NO!! Found a duplicate instrument Chunk!!");
  236.  
  237.                 if ( !(chunkFlags & kInstrument) ) {                                                        //  see if this chunk already exists
  238.                     chunkFlags |= kInstrument;                                                            //        otherwise mark it found
  239.                     filePosition += template->instrument.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  240.                     err = noErr;
  241.                 }
  242.                 break;
  243.  
  244.  
  245.             case MIDIDataID:
  246.                 DebugMessage ("\pfound a MIDI Data chunk");
  247.                 Assert ((chunkFlags & kMIDIData), "\pOH NO!! Found a duplicate MIDI Chunk!!");
  248.  
  249.                 if ( !(chunkFlags & kMIDIData) ) {                                                            //  see if this chunk already exists
  250.                     chunkFlags |= kMIDIData;                                                            //        otherwise mark it found
  251.                     filePosition += template->midiData.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  252.                     err = noErr;
  253.                 }
  254.                 break;
  255.  
  256.  
  257.             case AudioRecordingID:
  258.                 DebugMessage ("\pfound an audio recording chunk");
  259.                 Assert ((chunkFlags & kAudioRecording), "\pOH NO!! Found a duplicate Audio Recording Chunk!!");
  260.  
  261.                 if ( !(chunkFlags & kAudioRecording) ) {                                                    //  see if this chunk already exists
  262.                     chunkFlags |= kAudioRecording;                                                        //        otherwise mark it found
  263.                     filePosition += template->audioRecording.ckSize + kChunkHeaderSize;                    //        calculate next chunk's position
  264.                     err = noErr;
  265.                 }
  266.                 break;
  267.  
  268.  
  269.             case ApplicationSpecificID:
  270.                 DebugMessage ("\pfound an application specifi chunk");
  271.                 Assert ((chunkFlags & kApplicationSpecific), "\pOH NO!! Found a duplicate application specific Chunk!!");
  272.  
  273.                 if ( !(chunkFlags & kApplicationSpecific) ) {                                                //  see if this chunk already exists
  274.                     chunkFlags |= kApplicationSpecific;                                                    //        otherwise mark it found
  275.                     filePosition += template->generic.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  276.                     err = noErr;
  277.                 }
  278.                 break;
  279.  
  280.  
  281.             case NameID:
  282.                 DebugMessage ("\pfound a name chunk");
  283.                 Assert ((chunkFlags & kName), "\pOH NO!! Found a duplicate name Chunk!!");
  284.  
  285.                 if ( !(chunkFlags & kName) ) {                                                                //  see if this chunk already exists
  286.                     chunkFlags |= kName;                                                                //        otherwise mark it found
  287.                     filePosition += template->generic.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  288.                     err = noErr;
  289.                 }
  290.                 break;
  291.  
  292.  
  293.             case AuthorID:
  294.                 DebugMessage ("\pfound an author chunk");
  295.                 Assert ((chunkFlags & kAuthor), "\pOH NO!! Found a duplicate author Chunk!!");
  296.  
  297.                 if ( !(chunkFlags & kAuthor) ) {                                                            //  see if this chunk already exists
  298.                     chunkFlags |= kAuthor;                                                                //        otherwise mark it found
  299.                     filePosition += template->generic.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  300.                     err = noErr;
  301.                 }
  302.                 break;
  303.  
  304.  
  305.             case CopyrightID:
  306.                 DebugMessage ("\pfound a copyright chunk");
  307.                 Assert ((chunkFlags & kCopyright), "\pOH NO!! Found a duplicate copyright Chunk!!");
  308.  
  309.                 if ( !(chunkFlags & kCopyright) ) {                                                            //  see if this chunk already exists
  310.                     chunkFlags |= kCopyright;                                                            //        otherwise mark it found
  311.                     filePosition += template->generic.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  312.                     err = noErr;
  313.                 }
  314.                 break;
  315.  
  316.  
  317.             case AnnotationID:
  318.                 DebugMessage ("\pfound an uninteresting chunk");
  319.                 Assert ((chunkFlags & kAnnotation), "\pOH NO!! Found a duplicate annotation Chunk!!");
  320.  
  321.                 if ( !(chunkFlags & kAnnotation) ) {                                                        //  see if this chunk already exists
  322.                     chunkFlags |= kAnnotation;                                                            //        otherwise mark it found
  323.                     filePosition += template->generic.ckSize + kChunkHeaderSize;                        //        calculate next chunk's position
  324.                     err = noErr;
  325.                 }
  326.                 break;
  327.  
  328.  
  329.             default :                                                                                    // Unrecognized?? croak.
  330.                 Assert (true, "\pran into an undefined chunk!!");
  331.  
  332.                 //    If we hit an unrecognized chunk, clear chunkFlags to drop out of the loop
  333.                 //  with the assumed error code.
  334.                 chunkFlags = 0;
  335.                 break;
  336.         }
  337.  
  338.     //    We're only done when the the FORM, FormatVersion, Common and Sound Data chunks.  This is only
  339.     //    true of this incarnation - your needs may vary, so you'll have to modify the following statement
  340.     //    accordingly...
  341.     //  I know, this clause is a pain in the ass:  what it really says is this:
  342.     //        As long as we have a FORM Chunk and we don't have all of the {Formatversion, Common, SoundData} chunks
  343.     //        keep going... AND we haven't gotten an error from the file system.
  344.     } while (    (chunkFlags & kFORM)
  345.                 && (!(chunkFlags & kCommon) || !(chunkFlags & kSoundData))
  346.                 && (err == noErr) );
  347.  
  348.     //    If we completed normally, Position the mark at the beginning of the sound data
  349.     if (chunkFlags && (err == noErr) ) {
  350.         err = SetFPos (frefNum, fsFromStart, dataStart);                                                // move mark to data start
  351.         Assert ((err != noErr), "\pSetFPos failed");
  352.     }
  353.     if (err != noErr)
  354.         dataSize = 0;                                // signal a failure
  355.     return (dataSize);
  356. }
  357.  
  358. //
  359. //            Records from the currently selected sound input device into a file.
  360. //            The error you get back can be from standardfile, file manager or the sound manager
  361. //
  362. OSErr RecordAIFFFile (OSType creator)
  363. {
  364.     short                     fRefNum;
  365.     OSErr                    err;
  366.     Point                    here;
  367.     StandardFileReply        sReply;
  368.  
  369.     StandardPutFile ("\pRecord to:", "\pNew AIFF File", &sReply);
  370.     Assert ((!sReply.sfGood), "\pFailed in standardfile");
  371.  
  372.     if (sReply.sfGood) {                                                    // Valid file selected?
  373.     
  374.         //    Standard file survived the user!! Decide if the file exists or needs to be created
  375.         if (sReply.sfReplacing) {
  376.  
  377.             err = FSpDelete (&sReply.sfFile);
  378.             Assert (err, "\pCouldn't delete existing file");
  379.         }
  380.  
  381.         //    Now create a new file and then open it...
  382.         err = FSpCreate (&sReply.sfFile, creator, 'AIFF',iuSystemScript);
  383.         Assert (err, "\pCouldn't create file");
  384.         if (err == noErr) {
  385.             err = FSpOpenDF (&sReply.sfFile, fsRdWrPerm, &fRefNum);
  386.             Assert (err, "\pCouldn't open file");
  387.         }
  388.     } else
  389.         err = userCanceledErr;                                                                                    //   No! return error...
  390.  
  391.     if (err == noErr) {
  392.     
  393.         //    record to the file using the highest level routines available.  This gives
  394.         //    us a ready made user interface....
  395.         here.h = 30;
  396.         here.v = 30;
  397.         err = SndRecordToFile(nil, here, siBestQuality, fRefNum);
  398.         Assert (err, "\pBombed out recording...");
  399.     
  400.         //    Now that we're done (for better or for worse) close the file...
  401.         FSClose (fRefNum);
  402.     }
  403.     return (err);
  404. }
  405.